﻿
namespace Leetcode.N0591;
public class Solution
{
    public bool IsValid(string code)
    {
        try
        {
            this.Check(code);
        }
        catch
        {
            return false;
        }
        return true;
    }

    private void Check(string code)
    {
        var stack = new Stack<string>();
        for (int i = 0; i < code.Length; i++)
        {
            switch (code[i])
            {
                case '<':
                    if (i < code.Length - 1 && code[i + 1] == '!')
                    {
                        if (stack.Count == 0)
                        {
                            throw new Exception("1.代码必须被合法的闭合标签包围。否则，代码是无效的。");
                        }

                        this.ReadCData(code, ref i);
                    }
                    else
                    {
                        var tag = this.ReadTag(code, ref i);
                        if (tag.isStart)
                        {
                            stack.Push(tag.tagName);
                        }
                        else
                        {
                            if (stack.Count == 0 || stack.Pop() != tag.tagName)
                            {
                                throw new Exception("2.闭合标签（不一定合法）要严格符合格式：<TAG_NAME>TAG_CONTENT</TAG_NAME>");
                            }

                            if (stack.Count == 0 && i != code.Length - 1)
                            {
                                throw new Exception("1.代码必须被合法的闭合标签包围。否则，代码是无效的。");
                            }
                        }
                    }
                    break;
                default:
                    if (stack.Count == 0)
                    {
                        throw new Exception("1.代码必须被合法的闭合标签包围。否则，代码是无效的。");
                    }

                    break;
            }
        }


        if (stack.Count > 0)
        {
            throw new Exception("2.闭合标签（不一定合法）要严格符合格式：<TAG_NAME>TAG_CONTENT</TAG_NAME>");
        }
    }

    private (string tagName, bool isStart) ReadTag(string code, ref int index)
    {
        int tagNameCount = 0;
        bool hasEnd = false;
        bool isStartTagName = true;

        if (index < code.Length - 1 && code[index + 1] == '/')
        {
            isStartTagName = false;
            index++;
        }

        for (int i = index + 1; i < code.Length; i++)
        {
            if (code[i] == '>')
            {
                hasEnd = true;
                break;
            }

            if (code[i] >= 'A' && code[i] <= 'Z')
            {
                tagNameCount++;
                if (tagNameCount > 9)
                {
                    throw new Exception($"3.合法的 TAG_NAME长度在范围 [1,9] 之间");

                }
                continue;
            }

            throw new Exception($"3.合法的 TAG_NAME 仅含有大写字母");
        }

        if (!hasEnd)
        {
            throw new Exception($"6.一个<，如果你找不到一个后续的>与之匹配，是不合法的");
        }

        if (tagNameCount == 0)
        {
            throw new Exception("合法的 TAG_NAME 仅含有大写字母，长度在范围 [1,9] 之间");
        }

        var tagName = code.Substring(index + 1, tagNameCount);
        index += tagNameCount + 1;
        return (tagName, isStartTagName);
    }

    private void ReadCData(string code, ref int index)
    {
        const string Prefix = "![CDATA[";
        for (int i = 0; i < Prefix.Length; i++)
        {
            index++;
            if (code[index] != Prefix[i])
            {
                throw new Exception("7.cdata 有如下格式：<![CDATA[CDATA_CONTENT]]>。");
            }
        }

        bool hasEnd = false;
        for (int i = index + 1; i < code.Length; i++)
        {

            if (code[i] == ']' && i + 2 < code.Length && code[i + 1] == ']' && code[i + 2] == '>')
            {
                index = i + 2;
                hasEnd = true;
                break;
            }
        }

        if (!hasEnd)
        {
            throw new Exception("7.cdata 有如下格式：<![CDATA[CDATA_CONTENT]]>。");
        }
    }

}